{{extend 'layout.html'}} {{import os}}

<h1>web2py<sup style="font-size:0.5em;">TM</sup> Tools</h1>

<p>Since version 1.56 web2py includes tools for <a href="#authentication">authentication</a>, <a href="#authorization">authorization</a>, <a href="#crud">CRUD</a>, and <a href="#other">more</a>. They are implemented so that they do not require JOINs and work on Google App Engine as well. </p>

<h2 id="authentication">Authentication</h2>

<p>The basic authentication tool is the <b>Auth</b> class. It provides methods that can be used as controller actions to register users (with optional <a href="http://recaptcha.net/" target=_blank>Recaptcha</a> support), log them in and out, allow email verification, password change, password reset and retrieval, editing user profile.</p>

<p>These functionalities can then be used as the basis for <a href="{{URL(r=request,c='default',f='#authorization')}}">authorization</a>.</p>

<p>The Auth calls can be extended, personalized, and replaced by other authentication mechanisms which expose a similar interface.</p>

<p>To use authentication, write something like this in a model file:</p>

{{=CODE("""
from gluon.tools import Mail, Auth, Recaptcha

mail=Mail()
## specify your SMTP server
mail.settings.server = 'smtp.yourdomain.com:25'
## specify your email address
mail.settings.sender = 'you@yourdomain.com'
## optional: specify the username and password for SMTP
mail.settings.login = 'username@password'

## instantiate the Auth class (or your derived class)
auth = Auth(globals(), db)
## ask it to create all necessary tables
auth.define_tables()
## optional: require email verification for registration
# auth.settings.mailer = mail
## optional: if you require captcha verification for registration
# auth.settings.captcha = Recaptcha(request,public_key='RECAPTCHA_PUBLIC_KEY',private_key='RECAPTCHA_PRIVATE_KEY')
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}

<p>In your controller (for example in <b>default.py</b>) expose the <b>auth</b> object (for example via a <b>user</b> action):

{{=CODE("""
def user():
    return dict(form = auth())
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}

<p>The above action will expose the following URLs</p>
<ul>
<li>http://locahost:8000/application/default/user/register</li>
<li>http://locahost:8000/application/default/user/login</li>
<li>http://locahost:8000/application/default/user/logout</li>
<li>http://locahost:8000/application/default/user/verify_email</li>
<li>http://locahost:8000/application/default/user/profile</li>
<li>http://locahost:8000/application/default/user/change_password</li>
<li>http://locahost:8000/application/default/user/retrieve_password</li>
<li>http://locahost:8000/application/default/user/groups</li>
</ul>

<p>The groups page shows a list of roles and description for the groups the logged-in user is a member of (see <a href="#authorization">authorization</a>).</p>

<p>You can check if a user is logged in via <b>auth.is_logged_in()</b>. If a user is logged in its record information can be found in <b>auth.user</b>. By default a user is stored in a table called "auth_user" and has the following columns: first_name, last_name, email, password. Password is stored encrypted (md5 hashed or stronger). You can change the table name, define your own table with more fields, require additional validators. </p>

<p>All authentication events are logged in a table called "auth_event".</p> 

<h3>Custom Authentication</h3>

<p>You can customize the Auth class by extending it. Here is an example of code you can put in your model to include internationalization for authentication messages:</p>

{{=CODE("""
from gluon.tools import Auth
class MyAuth(Auth):
    def __init__(self, environment, T, db = None):
        "Initialise parent class & make any necessary modifications"
        Auth.__init__(self,environment,db)
        self.messages.logged_in = T("Logged in")
        self.messages.email_sent = T("Email sent")
        self.messages.email_verified = T("Email verified")
        self.messages.logged_out = T("Logged out")
        self.messages.registration_successful = T("Registration successful")
        self.messages.invalid_email = T("Invalid email")
        self.messages.invalid_login = T("Invalid login")
        self.messages.verify_email_subject = T("Password verify")
        self.messages.username_sent = T("Your username was emailed to you")
        self.messages.new_password_sent = T("A new password was emailed to you")
        self.messages.password_changed = T("Password changed")
        self.messages.retrieve_username=str(T("Your username is"))+": %(username)s"
        self.messages.retrieve_username_subject="Username retrieve"
        self.messages.retrieve_password=str(T("Your password is"))+": %(password)s"
        self.messages.retrieve_password_subject = T("Password retrieve")
        self.messages.profile_updated = T("Profile updated")

auth = MyAuth(globals(), T, db)
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}

<p>Views are handled the usual web2py way.</p>

<h2 id="authorization">Authorization</h2>

<p>Once you have a user identified by <b>user.id</b>, you can create a group (for example "Manager")</p>

{{=CODE("""
group_id = auth.add_group(role = "Manager", description = "example of a group")
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'),counter=None)}}

<p>make the user a member:</p>

{{=CODE("""
auth.add_membership(group_id,user_id)
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'),counter=None)}}

<p>and assign permissions to all members of the group:</p>

{{=CODE("""
auth.add_permission(group_id,'create','tablename',record_id)
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'),counter=None)}}

<p>Now you can enforce permissions using the following decorators:</p>

{{=CODE("""
auth.settings.on_failed_authorization=URL(r=request,f='error')

@auth.requires_login():
def some_function1():
    return dict()

@auth.requires_membership('Manager'):
def some_function2():
    return dict()

@auth.requires_permission('create','tablename',1):
def some_function3():
    return dict()

def error():
    return dict(message = T("not authorized"))
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}

<p>Group roles are conventional. Permission names are also conventional. If one group has a certain permission on record_id==0 (default), it means the user has the permission on any record.<p>

<p>Some permission names ("tables", "select", "create", "update", "delete", "read") have a special meaning because they can be automatically enforced by the CRUD tool described below.</p>

<h2 id="crud">Create/Read/Update/Delete and more</h2>

<p>To use CRUD, in your model insert the following code:</p>

{{=CODE("""
from gluon.tools import Crud
crud = Crud(globals(),db)
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}

<p>and in a controller (for example in <b>default.py</b>) expose it via an action (for example <b>data</b>):</p>

{{=CODE("""
def data():
    return dict(form = crud())

def download():
    return response.download(request, db)
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}

<p>This will expose the following URLs:</p>

<ul>
<li>http://locahost:8000/application/default/data/tables</li>
<li>http://locahost:8000/application/default/data/select/<i>tablename</i></li>
<li>http://locahost:8000/application/default/data/create/<i>tablename</i></li>
<li>http://locahost:8000/application/default/data/read/<i>tablename</i>/<i>record_id</i></li>
<li>http://locahost:8000/application/default/data/update/<i>tablename</i>/<i>record_id</i></li>
<li>http://locahost:8000/application/default/data/delete/<i>tablename</i>/<i>record_id</i></li>
<li>http://locahost:8000/application/default/download/<i>filename</i></li>
</ul>

<p>"tables" list of current database tables.</p>

<p>"download" only allows to download uploaded files (whether they are in the uploads folder or in the database).</p>


<p>To enforce authorization on these CRUD URLs simply set</p>
{{=CODE("""
crud.settings.auth=auth
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}

<p>and the logged in user will only be able to "create" a record in table "tablename" if the user is a member of a group that has "create" permission on table "tablename". The same mechanism works for tables, select, read, update, and delete URLs.</p>

<h2 id="fetch">Fetch a URL</h2>

<p>The Python module urllib does not work well on the Google App Engine. For this reason we created a portable function for fetching url that works everywhere, including GAE:</p>

{{=CODE("""
from gluon.tools import fetch
html = fetch("http://www.web2py.com")
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}


<h2 id="geocode">Geocoding</h2>

<p>Another very common need of modern web applications is that of converting an address into latitude and longitude. We also provide a portable function to do it that uses the Google Geocoding API:</p>

{{=CODE("""
from gluon.tools import geocode
(latitude, longitude) = geocode("243 S Wabash Ave, Chicago IL 60604, USA")
""".strip(),language='web2py',link=URL(r=request,c='global',f='vars'))}}

<p>It returns 0,0 on failure.</p>
